package mathy.wili.extract_srcFile.java;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import javassist.NotFoundException;
import mathy.c.Ca;
import mathy.wili.autoeq.Refs;
import mathy.wili.c.Class9;
import mathy.wili.c.Javaassist9;
import mathy.wili.c.MiscFilter.ClsFilter;
import mathy.wili.c.MiscFilter.ObjFilter;
import mathy.wili.c.types.IsRet;
/**
 * 
 * @author weila 2021年2月19日
 */
public class ClsRange extends ClsMemberRange {
	private static final long serialVersionUID = 1L;

	int leftBraceNo;

	ClsMemberRange lastMember;

	public final Class<?> cls;

	public boolean staticInited = false, objInited = false;//静态块和对象初始块是否被访问过。
	public String toString() {
		//show class's nesting range.
		StringBuilder sb = new StringBuilder();
		sb.append(this.toMemberString() + "cls:" + this.cls.getSimpleName());
		for (int i = 0; i < sons.size(); i++) {
			sb.append("\n" + sons.get(i));
		}
		String string = sb.toString();
		return string;
	}
	public final Map<String, FieldRange> fieldMap = new HashMap<>();

	private Map<String, MethodRange> methodMap;
	public void addField(FieldRange fd) {
		if (this.cls.equals(StaticFinalSimpleValue_fieldCheck.class))
			Ca.pause();
		FieldRange pre = fieldMap.put(fd.name, fd);
		if (pre != null)
			Ca.asert(false, cls.getName() + ",field:" + pre);
	}

	public FieldRange getFieldRange(String field) {
		return this.fieldMap.get(field);
	}

	public MethodRange getMethodRange(Executable md) {
		return this.getDeclaredMethodMap().get(md.toString());
	}

	public MethodRange getMethodRange(String name, Class<?>[] pmTypes) {
		Method md = Class9.getDeclaredMethod(-1, this.cls, name, pmTypes);
		if (md == null)
			return null;
		return this.getDeclaredMethodMap().get(md.toString());
	}

	public static ClsRange clsRangeOf(Class<?> cls) {
		Class<?> cls2 = Cont26.ClsToHeadClsMap.get(cls);
		if (cls2 != null && !cls2.equals(cls)) {
			cls = cls2;
		}
		RangesOfClsMember ret = RangesOfClsMember.getInst(-1, cls);
		if (ret == null)
			return null;
		if (ret.root == null)
			return null;
		return ret.root.getClsRangeMap().get(cls);
	}

	public boolean isEmpty() {
		return fieldMap.isEmpty() && sons.isEmpty() && (methodMap == null || methodMap.isEmpty());
	}

	/**
	 * 	若有则返回方法集，否则尝试生成（生成中确定各个方法的 Method对象）
	 */
	public Map<String, MethodRange> getDeclaredMethodMap() {
		if (methodMap != null)
			return methodMap;
		ClassPool cp = ClassPool.getDefault();
		methodMap = new HashMap<>();
		if (this.cls.isEnum())
			return methodMap;
		try {
			//通过 Javaaassist 把方法关联到其行号和起止下标。
			CtClass cls2 = cp.get(cls.getName());
			if (cls.equals(ObjFilter.class))
				Ca.pause();
			if (cls.equals(ClsFilter.class))
				Ca.pause();
			CtMethod[] mm2 = cls2.getDeclaredMethods();
			for (CtMethod ele : mm2) {
				int inc2 = ++inc;
				if (inc2 == 99)
					Ca.pause();
				Executable method = Javaassist9.ctMethod_toMethod(ele, cp);
				if (method == null && Refs.NULL())
					continue;
				if (method.isSynthetic())
					continue;//编译器生成的synthetic方法
				if (method.getName().equals("log"))
					if (this.cls.equals(IsRet.class))
						Ca.pause();
				int lineNo = ele.getMethodInfo2().getLineNumber(0);
				MethodRange range = root.findMethodRange(lineNo);
				if (range == null) {//某些内部类的方法行号离谱，故用此法，显然不可靠
					//比如：wili.c.AllotInt9$TwoBitIteratorConcator.next()行号为1，不对
					ele.getMethodInfo2().getLineNumber(0);
					range = root.findMethodRange(cls, method);
				}
				if (range == null && cls.isEnum())
					continue;
				//new MethodRange(this.root, lineNo, md);
				range.setMethod(method);
				methodMap.put(range.key, range);
			}
			CtConstructor[] cc = cls2.getDeclaredConstructors();
			for (CtConstructor ele : cc) {
				Executable method = Javaassist9.ctMethod_toMethod(ele, cp);
				if (method == null && Refs.NULL())
					continue;
				int lineNo = ele.getMethodInfo().getLineNumber(0);
				MethodRange range = root.findMethodRange(lineNo);
				if (range == null) {
					range = root.findMethodRange(cls, method);
					if (range == null && Refs.NULL())
						continue;
				}
				range.setMethod(method);
				methodMap.put(range.key, range);
			}
			Class<?>[] incc = cls.getDeclaredClasses();
			Map<Class<?>, ClsRange> cMpa = this.root.getClsRangeMap();
			for (Class<?> inCls : incc) {
				cMpa.get(inCls).getDeclaredMethodMap();
			}
		} catch (NotFoundException e) {
			e.printStackTrace();
		}
		return methodMap;
	}

	public ClsRange(RootOfClsMember root, Matcher mat, ClsRange dad, String name, int bodyStart) {
		super(root, mat);
		if (dad != null) {
			if (dad.sons == null)
				dad.sons = new ArrayList<>();
			dad.sons.add(this);
			Class<?> meCls = null;
			Class<?>[] cc = dad.cls.getDeclaredClasses();
			if (name.equals("User"))
				Ca.pause();
			for (Class<?> ele : cc) {
				if (ele.getSimpleName().equals(name)) {
					meCls = ele;
					break;
				}
			}
			if (meCls == null) {
				String st = dad.cls.getName() + "." + name;
				this.cls = Class9.classOfName(st, -1);
			} else {
				this.cls = meCls;
			}
		} else if (name.equals(root.clazz.getSimpleName())) {
			this.cls = root.clazz;
		} else {
			String st = root.clazz.getPackageName() + "." + name;
			this.cls = Class9.classOfName(st, 2);
		}
		Ca.asert(cls != null, name);
		this.clsName = name;
		root.clsRangeMap.put(this.cls, this);
		this.depth = dad == null ? 1 : dad.depth + 1;
		this.bodyStart = bodyStart;
	}
	static int inc;
}